package FIT::TypeAdapter;

use FIT::Class;
use aliased 'FIT::Util::ClassLoader';
use aliased 'FIT::TypeAdapter::Null';
use FIT::Util::Constraint -All;
use base 'Exporter';

our @EXPORT     = qw(adapt);
my @Child_Types = qw(Str Num Bool);

sub adapt(%) {
    my (%params) = @_;

    # if there is no selector we ignore the element, using the null adapter
    # useful for column fixture that have empty separator columns
    return Null->new if $params{name} eq '';

    my $name          = $params{name};
    my $adaptee       = $params{adaptee};
    my $type_from     = $params{type_from};
    my $from          = ($type_from || $adaptee);
    my $meta          = $from->meta                          || die "can't adapt [$from] because it has no meta class";
    my $attribute     = $meta->find_attribute_by_name($name) || die "can't adapt [$from] because it has no attribute [$name]";
    my $type          = $attribute->type_constraint;
    my $adapter_class = load_adapter_class_for($type);

    return $adapter_class->new(%params, type_constraint => $type);
}

sub load_adapter_class_for() {
    my $type = shift;
    my $adapter_class = "FIT::TypeAdapter::$type";
    ClassLoader->load($adapter_class);
    return $adapter_class;
}

# FIT types --------------------------------------------------------------------

# a result of a test run
subtype 'TestResult'
    => as 'Str'
    => where { $_ eq 'OK' or $_ eq 'FAIL' }
    => message { "TestResult [$_] can only be OK or FAIL" };

# a relative link from the input page to a file
subtype 'InputFile'
    => as 'Str'
    => where { !m|^/| }
    => message { "The input file [$_] is not specfied as a link relative to test input page" };

# create ArrayRef of basic types
my @Child_Methods = qw(as_string parse_from_string compare);
for my $Child_Type (@Child_Types) {
    local $_;
    my $child_profile = MooseType($Child_Type);
    my $profile       = IsArrayRef($child_profile);
    my $child_adapter = "FIT::TypeAdapter::$Child_Type";
    my $subtype       = "ArrayRefOf$Child_Type";

    ClassLoader->load($child_adapter);
    
    # create subtype
    my $result;
    subtype $subtype
        => as 'ArrayRef'
        => where { 
            $result = undef; # in case next line fails
            $result = $profile->($_);
            return $result->is_valid;
        }
        => message { $result->message. " at path: ". $result->path };

    # create type adapter
    ClassLoader->create(
        name    => "FIT::TypeAdapter::$subtype",
        base    => 'FIT::TypeAdapter::ArrayRef',
        methods => {
            map {
                my $method = $_;
                ("child_$_" => sub { shift; $child_adapter->$method(@_) } );
            } @Child_Methods
        },
    );        
}

